package com.fxu.framework.biz.aop;

import ch.qos.logback.classic.Level;
import com.fxu.framework.biz.ano.SqlLogger;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 打印或隐藏SQL日志
 * @author fangxu
 * @version 1.0.0
 */
@Slf4j
@Aspect
@Component
public class SqlLoggerAspect {
    private static final Map<String, ch.qos.logback.classic.Logger> LOGGER_MAP = new ConcurrentHashMap<>();

    @Pointcut("@annotation(com.fxu.framework.biz.ano.SqlLogger)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SqlLogger sqlLogger = method.getAnnotation(SqlLogger.class);

        Class<?>[] mapperClasses = sqlLogger.mappers();
        Level level = sqlLogger.hide() ? Level.WARN : Level.DEBUG;
        try {
            if (mapperClasses != null && mapperClasses.length > 0) {
                for (Class<?> mapperClass : mapperClasses) {
                    DynamicLogLevelManager.setLogLevel(mapperClass.getName(), level);
                }
            }
            return joinPoint.proceed();
        } finally {
            if (mapperClasses != null && mapperClasses.length > 0) {
                DynamicLogLevelManager.restoreLogLevels();
            }
        }
    }

    // 为了保证线程安全，使用线程管理器[多个线程互不影响]
    static class DynamicLogLevelManager {

        private static final ThreadLocal<Map<String, Level>> ORIGINAL_LEVELS = new ThreadLocal<>();

        public static void setLogLevel(String loggerName, Level targetLevel) {
            Map<String, Level> currentLevels = ORIGINAL_LEVELS.get();
            if (currentLevels == null) {
                currentLevels = new HashMap<>();
                ORIGINAL_LEVELS.set(currentLevels);
            }

            ch.qos.logback.classic.Logger logger = getLogger(loggerName);
            if (!currentLevels.containsKey(loggerName)) {
                currentLevels.put(loggerName, logger.getLevel());
            }
            logger.setLevel(targetLevel);
        }

        public static void restoreLogLevels() {
            Map<String, Level> originalLevels = ORIGINAL_LEVELS.get();
            if (originalLevels != null) {
                for (Map.Entry<String, Level> entry : originalLevels.entrySet()) {
                    getLogger(entry.getKey()).setLevel(entry.getValue());
                }
                ORIGINAL_LEVELS.remove();
            }
        }
    }

    public synchronized static ch.qos.logback.classic.Logger getLogger(String name) {
        if (LOGGER_MAP.containsKey(name)) {
            return LOGGER_MAP.get(name);
        }
        ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(name);
        LOGGER_MAP.putIfAbsent(name, logger); // 返回的null
        return logger;
    }
}
